package com.duoduo.common.api.help;


import com.duoduo.common.api.domain.SubtractListResult;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

public class ListInnerUtil {

    /**
     * 判断是否空list
     *
     * @param list
     * @return
     */
    public static <T> boolean isEmpty(Collection<T> list) {
        return list == null || list.isEmpty();
    }

    /**
     * 判断是否非空list
     *
     * @param list
     * @return
     */
    public static <T> boolean isNotEmpty(Collection<T> list) {
        return list != null && !list.isEmpty();
    }

    /**
     * 从list里面删除otherList里存在的元素，不修改原list，生成新的list
     *
     * @param list
     * @param otherList
     * @return
     */
    public static <T> List<T> removeElementInOtherList(List<T> list, List<T> otherList) {
        if (isEmpty(list) || isEmpty(otherList)) {
            return list;
        }
        Map<T, Integer> map = new HashMap<>();
        for (int i = 0; i < otherList.size(); i++) {
            map.put(otherList.get(i), i);
        }
        List<T> result = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            Integer pos = map.get(list.get(i));
            if (pos == null) {
                result.add(list.get(i));
            }
        }
        return result;
    }

    /**
     * 合并list，返回去除重复元素后的新list
     *
     * @param list1
     * @param list2
     * @return list1和list2全为空，则返回null
     */
    public static <T> List<T> removeListDupli(List<T> list1, List<T> list2) {
        if (list1 == null && list2 == null) {
            return null;
        }
        if (list1 == null) {
            return list2;
        }
        if (list2 == null) {
            return list1;
        }
        List<T> result = new ArrayList<>(list1);
        result.addAll(list2);
        return result.stream().distinct().collect(Collectors.toList());
    }

    /**
     * 合并多个list，不改变原始list，生成新的list
     *
     * @param lists 需要合并的list
     * @param <T>
     * @return
     */
    @SuppressWarnings({"unchecked"})
    public static <T> List<T> mergeLists(List<T>... lists) {
        List<T> list = new ArrayList<>();
        for (List<T> t : lists) {
            if (isNotEmpty(t))
                list.addAll(t);
        }
        return list;
    }

    /**
     * 合并多个list，去重重复，不改变原始list，生成新的list
     *
     * @param lists 需要合并的list
     * @param <T>
     * @return
     */
    @SuppressWarnings({"unchecked"})
    public static <T> List<T> mergeListsRemoveDupli(List<T>... lists) {
        List<T> list = new ArrayList<>();
        for (int i = 0, len = lists.length; i < len; i++) {
            Iterator<T> it = lists[i].iterator(); // 迭代器
            while (it.hasNext()) {
                T obj = it.next();
                if (!list.contains(obj)) {
                    list.add(obj);
                }
            }
        }
        return list;
    }

    public static List<String> strArrayToStringList(String[] arr) {
        return Arrays.stream(arr).map(id -> id).collect(toList());
    }

    /**
     * Collection<Long>转换为long[]
     *
     * @param list
     * @return
     */
    public static long[] longListToLongArray(Collection<Long> list) {
        if (list == null) {
            return null;
        }
        return list.stream().mapToLong(id -> id).toArray();
    }

    /**
     * Collection<Long>转换为Long[]
     *
     * @param list
     * @return
     */
    public static Long[] longListToLongArray2(Collection<Long> list) {
        if (list == null) {
            return null;
        }
        return list.stream().map(id -> id).toArray(Long[]::new);
    }

    /**
     * Collection<Long>转换为long[]
     *
     * @param list
     * @return
     */
    public static int[] intListToIntArray(Collection<Integer> list) {
        if (list == null) {
            return null;
        }
        return list.stream().mapToInt(id -> id).toArray();
    }

    /**
     * Integer[]转Collection<Integer>
     *
     * @param arr
     * @return
     */
    public static List<Integer> intArrayToIntList(Integer[] arr) {
        return Arrays.stream(arr).map(id -> id).collect(toList());
    }

    /**
     * Collection<Long>转换为Long[]
     *
     * @param list
     * @return
     */
    public static Integer[] intListToIntArray2(Collection<Integer> list) {
        if (list == null) {
            return null;
        }
        return list.stream().map(id -> id).toArray(Integer[]::new);
    }

    /**
     * stream转换long[]
     *
     * @param stream
     * @param mapper
     * @return
     */
    public static <T> long[] streamToLArr(Stream<T> stream, ToLongFunction<? super T> mapper) {
        return stream.mapToLong(mapper).toArray();
    }

    /**
     * stream转换int[]
     *
     * @param stream
     * @param mapper
     * @return
     */
    public static <T> int[] streamToIArr(Stream<T> stream, ToIntFunction<? super T> mapper) {
        return stream.mapToInt(mapper).toArray();
    }

    /**
     * stream转换int[]
     *
     * @param stream
     * @param mapper
     * @return
     */
    public static <T> int[] streamToIArr(Stream<T> stream, Predicate<? super T> predicate, ToIntFunction<? super T> mapper) {
        return stream.filter(predicate).mapToInt(mapper).toArray();
    }

    /**
     * stream转换long[]
     *
     * @param stream
     * @param mapper
     * @return
     */
    public static <T> long[] streamToLArr(Stream<T> stream, Predicate<? super T> predicate, ToLongFunction<? super T> mapper) {
        return stream.filter(predicate).mapToLong(mapper).toArray();
    }

    /**
     * stream转换Long[]
     *
     * @param stream
     * @param mapper
     * @return
     */
    public static <T, R> Long[] streamToLArr2(Stream<T> stream, Function<? super T, ? extends R> mapper) {
        return stream.map(mapper).toArray(Long[]::new);
    }

    /**
     * stream转换Long[]
     *
     * @param stream
     * @param mapper
     * @return
     */
    public static <T, R> Integer[] streamToIArr2(Stream<T> stream, Function<? super T, ? extends R> mapper) {
        return stream.map(mapper).toArray(Integer[]::new);
    }

    /**
     * list转换long[]
     *
     * @param list
     * @param mapper
     * @return
     */
    public static <T> long[] listToLArr(Collection<T> list, ToLongFunction<? super T> mapper) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return streamToLArr(list.stream(), mapper);
    }

    public static <T> String[] listToSArr(Collection<String> list) {
        if (list == null || list.size() == 0) {
            return new String[0];
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * list转换int[]
     *
     * @param list
     * @param mapper
     * @return
     */
    public static <T> int[] listToIArr(Collection<T> list, ToIntFunction<? super T> mapper) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return streamToIArr(list.stream(), mapper);
    }

    /**
     * list转换Long[]
     *
     * @param list
     * @param mapper
     * @return
     */
    public static <T, R> Long[] listToLArr2(Collection<T> list, Function<? super T, ? extends R> mapper) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return streamToLArr2(list.stream(), mapper);
    }

    /**
     * list转换Long[]
     *
     * @param list
     * @param mapper
     * @return
     */
    public static <T, R> Integer[] listToIArr2(Collection<T> list, Function<? super T, ? extends R> mapper) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return streamToIArr2(list.stream(), mapper);
    }

    /**
     * list转换List<Integer>
     *
     * @param list
     * @param mapper
     * @return
     */
    public static <T> List<Integer> listToIList(Collection<T> list, Function<? super T, Integer> mapper) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.stream().map(mapper).collect(toList());
    }

    /**
     * @param stream
     * @param mapper
     * @description: stream转换List
     * @return: java.util.List<java.lang.Integer>
     * @date: 2022/8/26 13:46
     */
    public static <T> List<Integer> streamToIList(Stream<T> stream, Function<? super T, Integer> mapper) {
        if (stream == null) {
            return null;
        }
        return stream.map(mapper).collect(toList());
    }

    /**
     * list转换List<Long>
     *
     * @param list
     * @param mapper
     * @return
     */
    public static <T> List<Long> listToLList(Collection<T> list, Function<? super T, Long> mapper) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.stream().map(mapper).collect(toList());
    }

    /**
     * @param stream
     * @param mapper
     * @description: stream转换List
     * @return: java.util.List<java.lang.Long>
     * @date: 2022/8/26 13:45
     */
    public static <T> List<Long> streamToLList(Stream<T> stream, Function<? super T, Long> mapper) {
        if (stream == null) {
            return null;
        }
        return stream.map(mapper).collect(toList());
    }

    /**
     * @param list
     * @param r
     * @description: list<T>类型强转换
     * @return: java.util.List<R>
     * @date: 2022/8/26 13:46
     */
    @SuppressWarnings("unchecked")
    public static <T, R> List<R> listChange(Collection<T> list, Class<R> r) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.stream().map(t -> (R) t).collect(toList());
    }

    /**
     * 去重
     */
    public static List<Integer> distinct(List<Integer> list) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.stream().distinct().collect(toList());
    }

    /**
     * 去重
     */
    public static <T> int[] distinct(List<T> list, ToIntFunction<? super T> mapper) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.stream().mapToInt(mapper).distinct().toArray();
    }

    /**
     * 去重
     */
    public static <T> List<Integer> distinctReturnList(List<T> list, ToIntFunction<? super T> mapper) {
        if (list == null || list.size() == 0) {
            return null;
        }
        return list.stream().mapToInt(mapper).distinct().boxed().collect(toList());
    }

    /**
     * 条件去重
     *
     * @param keyExtractor
     * @return
     */
    public static <T> List<T> distinct2(List<T> list, Function<? super T, ?> keyExtractor) {
        if (list == null || list.size() == 0) {
            return null;
        }
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        Predicate<T> p = t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
        return list.stream().filter(p).collect(toList());
    }

    /**
     * 条件去重(stream)
     *
     * @param keyExtractor, stream<T>
     * @return
     */
    public static <T> Stream<T> distinct2ReStream(Stream<T> stream, Function<? super T, ?> keyExtractor) {
        if (stream == null) {
            return null;
        }
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        Predicate<T> p = t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
        return stream.filter(p);
    }

    /**
     * @param f
     * @description: 获取list对象的泛型类型
     * @return: java.lang.Class<?>
     * @date: 2022/8/26 13:46
     */
    public static Class<?> getListEleType(Field f) {
        f.setAccessible(true);
        if (f.getType() == List.class) {
            // 如果是List类型，得到其Generic的类型
            Type genericType = f.getGenericType();
            // 如果是泛型参数的类型
            if (genericType != null && genericType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericType;
                //得到泛型里的class类型对象
                return (Class<?>) pt.getActualTypeArguments()[0];
            }
        }
        return null;
    }

    /**
     * 获取list对象的泛型类型
     *
     * @param type
     * @return
     */
    public static Class<?> getListEleType(Type type) {
        if (type == null)
            return null;
        if (!(type instanceof ParameterizedType)) {
            return null;
        }
        Type[] types = ((ParameterizedType) type).getActualTypeArguments();
        if (types != null && types.length > 0)
            return (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0];
        else
            return null;
    }

    /**
     * 获取list对象的泛型类型
     *
     * @param type
     * @return
     */
    public static Class<?> getListEleType(ParameterizedType type) {
        if (type == null)
            return null;
        Type[] types = type.getActualTypeArguments();
        if (types != null && types.length > 0)
            return (Class<?>) type.getActualTypeArguments()[0];
        else
            return null;
    }

    /**
     * 从list里返回num个随机元素组成的list，其实是随机生成一个起始位置，返回从起始位置往后的num个元素，所以num为0或list大小小于num，都会直接返回list
     *
     * @param list
     * @param num
     * @return
     */
    public static <T> List<T> shuffleNum(List<T> list, int num) {
        int listSize = list.size();
        if (listSize <= num || num == 0) {
            return list;
        }
        Random random = new Random();
        int skip = random.nextInt(listSize - num + 1);
        return list.stream().skip(skip).limit(num).collect(toList());
    }

    public static <T, K> List<T> kListToTList(List<K> kList, Function<? super K, ? extends T> map) {
        if (kList == null) {
            return null;
        }
        return kList.stream().map(map).collect(toList());
    }

    /**
     * 笛卡尔乘积算法
     * 把一个List{[1,2],[A,B],[a,b]} 转化成
     * List{[1,A,a],[1,A,b],[1,B,a],[1,B,b],[2,A,a],[2,A,b],[2,B,a],[2,B,b]} 数组输出
     *
     * @param dimensionValue 原List
     * @param result         通过乘积转化后的数组
     * @param layer          中间参数
     * @param currentList    中间参数
     */
    public static <T> void descartes(List<List<T>> dimensionValue, List<List<T>> result, int layer, List<T> currentList) {
        if (layer < dimensionValue.size() - 1) {
            if (dimensionValue.get(layer).size() == 0) {
                descartes(dimensionValue, result, layer + 1, currentList);
            } else {
                for (int i = 0; i < dimensionValue.get(layer).size(); i++) {
                    List<T> list = new ArrayList<T>(currentList);
                    list.add(dimensionValue.get(layer).get(i));
                    descartes(dimensionValue, result, layer + 1, list);
                }
            }
        } else if (layer == dimensionValue.size() - 1) {
            if (dimensionValue.get(layer).size() == 0) {
                result.add(currentList);
            } else {
                for (int i = 0; i < dimensionValue.get(layer).size(); i++) {
                    List<T> list = new ArrayList<T>(currentList);
                    list.add(dimensionValue.get(layer).get(i));
                    result.add(list);
                }
            }
        }
    }

    /**
     * 两个list里的元素依次互减，直到减到最后，例如：<br/>
     * tList	kList<br/>
     * 10		100<br/>
     * 50		40<br/>
     * 20<br/>
     * 60<br/>
     * 互减的结果是：<br/>
     * tList的索引	kList的索引	差<br/>
     * 0			0			10<br/>
     * 1			0			50<br/>
     * 2			0			20<br/>
     * 3			0			20<br/>
     * 3			1			40<br/>
     *
     * @param tList
     * @param kList
     * @return
     */
    public static List<SubtractListResult> subtractList(List<BigDecimal> tList, List<BigDecimal> kList) {
        if (isEmpty(kList)) {
            return null;
        }
        List<SubtractListResult> result = new ArrayList<>();
        int tIndex = 0;
        int kIndex = 0;
        BigDecimal tBig = new BigDecimal(tList.get(tIndex).toString());
        BigDecimal kBig = new BigDecimal(kList.get(kIndex).toString());
        int beforeIndex = 1;
        if (tBig.subtract(kBig).compareTo(BigDecimal.ZERO) < 0) {
            beforeIndex = 0;
        }
        BigDecimal tmp = null;
        while (tIndex < tList.size() && kIndex < kList.size()) {
            tmp = tBig.subtract(kBig);//差值
            SubtractListResult subtractList = null;
            if (tmp.compareTo(BigDecimal.ZERO) < 0) {
                if (beforeIndex == 0) {
                    subtractList = new SubtractListResult(tIndex, kIndex, tList.get(tIndex));
                } else {
                    //交界处
                    if (tBig.compareTo(BigDecimal.ZERO) != 0) {
                        subtractList = new SubtractListResult(tIndex, kIndex, tBig);
                    }
                }

                tIndex++;
                if (tIndex < tList.size()) {
                    tBig = new BigDecimal(tList.get(tIndex).toString());
                    kBig = tmp.abs();
                }
                beforeIndex = 0;
            } else {
                if (beforeIndex == 1) {
                    subtractList = new SubtractListResult(tIndex, kIndex, kList.get(kIndex));
                } else {
                    //交界处
                    if (kBig.compareTo(BigDecimal.ZERO) != 0) {
                        subtractList = new SubtractListResult(tIndex, kIndex, kBig);
                    }
                }

                kIndex++;
                if (kIndex < kList.size()) {
                    tBig = tmp;
                    kBig = new BigDecimal(kList.get(kIndex).toString());
                }
                beforeIndex = 1;
            }
            if (subtractList != null) {
                result.add(subtractList);
            }
        }
        return result;
    }

    /**
     * 判断是否是list、set容器
     *
     * @param obj
     * @return
     */
    public static boolean isCollection(Object obj) {
        return obj instanceof Collection;
    }

    /**
     * 过滤
     *
     * @param list
     * @param predicate
     * @return
     */
    public static <T> List<T> filter(List<T> list, Predicate<T> predicate) {
        if (isEmpty(list)) {
            return new ArrayList<>();
        }
        return list.stream().filter(predicate).collect(toList());
    }

}
